%{
/* 
=====================================================
   Especificacao Bison do Analisador Sintatico 
        para o Pascal Simplificado-Tomasz
-----------------------------------------------------
Autores:
	Andre Paulo Machado
	Elisangela de Assis da Silva
	Roberto Avelino da Silva
-----------------------------------------------------
Data: 13/03/2008
=====================================================
*/

#include <stdio.h>

#include "gerarMepa.h"
#include "tabSimbolosArray.c"
#include "pilhaInteiros.c"
#include "pilhaStrings.c"

#define ARQUIVO "mepa"

char arquivo_para_compilar[100];

int numVar = 0;  // contem o numero de variaveis declaradas
int contIds = 0;  // conta o numero de identificadores
int nvLexico = 0;	// nivel lexico => m
int endVar = -1;  // endereco lexico da variavel => n
int numPF = 0, numProcedInterno = -1;  // variaveil aux. para controle do numPF's 
int rotulo1, rotulo2;  // controle dos rotulos para condicionais e repetitivos
int i;   // variaveis auxiliares de uso geral ou para controle de lacos
int j = -1, k = -1, pos = -1;   // variaveis auxiliares no controle de cham. procedimento

int isDeclVars = 0;  // flag para declaracao de variaveis
int isLeit = 0;  // flag para entrada de dados - LEIT
int isImpr = 0;  // flag para saida de dados - IMPR
int isPF = 0;  // flag para controlar parametros formais
int isPorValor = 0;  // flag para controlar passagem de params. por valor
int isPorRefer = 0;  // flag para controlar passagem de params. por refer
int isBlocoInterno = 0; // flag para verificar se eh um bloco interno.
int isChamProced = 0;  //flag para controlar chamada de procedimento
int isCondicional = 0; 
int isDeclFunc = 0;  //flag para controlar declaracao de funcao

char strRotuloPaidoLabel[4]; //variavel para receber o rotuloPai onde o label foi declarado
char strRotulo[4];  // variavel auxiliar que contem a string com o ultimo rotulo
char strAux[6];  // variavel auxiliar nolmalmente usada para armazenar o nivel lexico ou nome rotulos
int vetAux[20], vetAux1[20];  // vetor auxiliar para armazenar o indice de params. formais na tab. simbolos
char mensagem[150];  // variavel auxiliar para exibicao de mensagens
char strTipo[8];  // variavel auxiliar nolmalmente usada para armazenar o tipo

int *pAux; // ponteiro auxiliar

TabelaSimbolos tabSimbolos, tabSimbolos_aux;
TabelaSimbolos *p_tabSimbolos, *p_tabSimbolos_aux;

ElemTS elemTS, elemTS_aux;

TPilha pilha_rotulos, pilha_vars; // pilhas para controle dos rotulos e das variaveis declaradas
TPilha *p_rotulos, *p_vars;

TPilhaString pilha_strings;
TPilhaString *p_strings;

%}

%union {
	int numToken;
	char* idToken;
}

%token <idToken> tIDENTIFICADOR
%token <numToken> tNUMERO

%token tPROGRAM tVAR tBEGIN tEND

%token tABRE_PARENTESE tFECHA_PARENTESE
%token tREAD tWRITE
%token tVIRGULA tPONTO_E_VIRGULA
%token tPONTO tDOIS_PONTOS
%token tATRIBUI tMAIS tMENOS tMULTIPLICA tDIV tOR tAND tNOT
%token tIGUAL tDIFERENTE tMENOR tMENOR_OU_IGUAL tMAIOR tMAIOR_OU_IGUAL
%token tWHILE tDO tREPEAT tUNTIL
%token tPROCEDURE tFUNCTION
%token tLABEL tGOTO
%token tIF tTHEN tELSE tTRUE tFALSE
%token <idToken> tINTEGER 
%token <idToken> tBOOLEAN

%nonassoc LOWER_THAN_ELSE
%nonassoc tELSE

%type <idToken> lista_ids
%type <idToken> variavel

%start programa

%%

programa:	
	tPROGRAM tIDENTIFICADOR tABRE_PARENTESE lista_ids tFECHA_PARENTESE tPONTO_E_VIRGULA 
		{ geraMepa("INPP"); }
	bloco tPONTO 
		{ numVar = desempilha_int( p_vars );
		  geraMepa1("DMEM", numVar); geraMepa("PARA");
		  while (!(vazia_int( p_rotulos ))){
			desempilha_int( p_rotulos );
			} // Limpa a pilha de rotulos gerados do while
		}
;

lista_ids:
 	tIDENTIFICADOR 
 		{ contIds = 1;  
 		  if ( isDeclVars )  {
 		  	  endVar++;
			  
			  // verifica se o simbolo ja' foi inserido na TS
			  i = busca_elemento_nivel( $1, nvLexico, p_tabSimbolos );
 		      if ( i == -1 ) {  // nao achou outro simbolo igual, entao insere na TS
				  strcpy( elemTS.ident, $1 );
				  elemTS.categ = categ_vs;
				  elemTS.nivel = nvLexico;
				  sprintf( elemTS.ender_lexico, "%d, %d", nvLexico, endVar );
				  empilha( elemTS, p_tabSimbolos );
			  }
			  else {  // achou outro simbolo igual
				  sprintf( mensagem, "Erro: Identificador \"%s\" esta' duplicado", $1 );
				  yyerror( mensagem );
				  exit(1);
			  }
 		  }
 		  if ( isLeit ) {
 		  	geraMepa("LEIT"); 
 		  	i = busca_elemento( $1, p_tabSimbolos );
		  	if ( i != -1 ) 
		    	geraMepa4("ARMZ", p_tabSimbolos->elem[i].ender_lexico );
		    else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  	}	
 		  }
 		  if ( isPF ) {
 		  	  j++;
 		  	  
 		  	  /* cria elemento na tab. de simbolos   */
 		  	  strcpy( elemTS.ident, $1 );
 		  	  elemTS.categ = categ_pf;
			  elemTS.nivel = nvLexico; 
			  
 		  	  if ( !isDeclFunc ) {  // e' declaracao de procedimento
				  if ( isPorValor ) {
					  elemTS.u_categ.pf.passagem_parametros = por_valor;
					  vetAux[j] = por_valor;
				  }
				  else if ( isPorRefer ) {
					  elemTS.u_categ.pf.passagem_parametros = por_refer;
					  vetAux[j] = por_refer;
				  }
			  }
			  else {   // e' declaracao de funcao
				  if ( isPorValor ) {
					  elemTS.u_categ.pf.passagem_parametros = por_valor;
					  vetAux1[j] = por_valor;
				  }
				  else if ( isPorRefer ) {
					  elemTS.u_categ.pf.passagem_parametros = por_refer;
					  vetAux1[j] = por_refer;
				  }
			  }
 		  	  empilha( elemTS, p_tabSimbolos_aux );
 		  } 		   		  
 		}
 	lista_ids_zrepet
;

lista_ids_zrepet:
	lista_ids_zrepet tVIRGULA tIDENTIFICADOR 
		{ contIds++;
		  if ( isDeclVars )  {
 		      endVar++;

			  // verifica se o simbolo ja' foi inserido na TS
			  i = busca_elemento_nivel( $3, nvLexico, p_tabSimbolos );
 		      if ( i == -1 ) {  // nao achou outro simbolo igual, entao insere na TS
				  strcpy( elemTS.ident, $3 );
				  elemTS.categ = categ_vs;
				  elemTS.nivel = nvLexico;
				  sprintf( elemTS.ender_lexico, "%d, %d", nvLexico, endVar );
				  empilha( elemTS, p_tabSimbolos );
			  }
			  else {
				  sprintf( mensagem, "Erro: Identificador \"%s\" esta' duplicado", $3 );
				  yyerror( mensagem );
				  exit(1);
			  }
 		  }
 		  if ( isLeit ) {
 		  	geraMepa("LEIT"); 
 		  	i = busca_elemento( $3, p_tabSimbolos );
		  	if ( i != -1 ) 
		    	geraMepa4("ARMZ", p_tabSimbolos->elem[i].ender_lexico ); 
		    else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $3 );
			  yyerror( mensagem );
		  	  exit(1);
			}	
 		  }
 		  if ( isPF ) {
 		  	  j++;
 		  	  
 		  	  /* cria elemento na tab. de simbolos   */
 		  	  strcpy( elemTS.ident, $3 );
 		  	  elemTS.categ = categ_pf;
			  elemTS.nivel = nvLexico; 
			  
			  if ( !isDeclFunc ) {  // e' declaracao de procedimento
				  if ( isPorValor ) {
					  elemTS.u_categ.pf.passagem_parametros = por_valor;
					  vetAux[j] = por_valor;
				  }
				  else if ( isPorRefer ) {
					  elemTS.u_categ.pf.passagem_parametros = por_refer;
					  vetAux[j] = por_refer;
				  }
			  }
			  else {   // e' declaracao de funcao
				  if ( isPorValor ) {
					  elemTS.u_categ.pf.passagem_parametros = por_valor;
					  vetAux1[j] = por_valor;
				  }
				  else if ( isPorRefer ) {
					  elemTS.u_categ.pf.passagem_parametros = por_refer;
					  vetAux1[j] = por_refer;
				  }
			  }
 		  	  empilha( elemTS, p_tabSimbolos_aux );
 		  }  		 
 		}
	| vazio
;

bloco:	
	parte_opcional comando_composto
;

vazio: /* vazio! */
;

parte_decl_vars:
		{ isDeclVars = 1; numVar = 0; }
	tVAR decl_vars decl_vars_zrepet tPONTO_E_VIRGULA
		{ empilha_int( numVar, p_vars );
		  geraMepa1("AMEM", numVar);  

		  isDeclVars = 0;
	  	  endVar = -1;
		}
;

decl_vars_zrepet:
	decl_vars_zrepet tPONTO_E_VIRGULA decl_vars
	| vazio
;

decl_vars:
	lista_ids tDOIS_PONTOS tipo
;

tipo: 
	tINTEGER
	{ /* insere o tipo do identificador dos elementos na TS  */
              numVar += contIds;
              for ( i = p_tabSimbolos->topo; i >= ( ( p_tabSimbolos->topo + 1 ) - contIds ); i-- ) {
                    strcpy( p_tabSimbolos->elem[i].tipo, "integer" );
              }
	 }
        | tBOOLEAN
	 	{ /* insere o tipo do identificador dos elementos na TS  */
		  	numVar += contIds;
			for ( i = p_tabSimbolos->topo; i >= ( ( p_tabSimbolos->topo + 1 ) - contIds ); i-- ) {
			    strcpy( p_tabSimbolos->elem[i].tipo, "boolean" );
			}
		}
;

	

comando_composto:	
	tBEGIN
		{
		  if ( isBlocoInterno == 0 ) {
		  	rotulo1 = desempilha_int( p_rotulos );
		  	sprintf( strRotulo, "R%02d", rotulo1 );
		  	geraMepa5( strRotulo, "NADA" );
		  }
		}
	comando	comando_zrepet tEND
;

comando_zrepet:
	comando_zrepet tPONTO_E_VIRGULA comando
	| vazio
;

comando:
	tNUMERO 
		{ sprintf( strAux, " %d ", $1 ); }
	tDOIS_PONTOS 
		{ 
		  i = busca_elemento( strAux, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_label ) ) {
			strcpy(strRotulo, p_tabSimbolos->elem[i].u_categ.lab.rot_interno);
			
			if ( p_tabSimbolos->elem[i].nivel == 0 ) {
				i = busca_numero_varslocal_no_nivel( 0 , p_tabSimbolos );
				geraMepa5( strRotulo, "NADA" );
				geraMepa2("ENRT", nvLexico, i ); // ENRT nvLexico, numVarsLocais_ao_Rotulo
			}
			else if ( p_tabSimbolos->elem[i].nivel > 0 ) {
				i = busca_numero_varslocal_ao_rotulo( p_tabSimbolos->elem[i].u_categ.lab.rot_pai, p_tabSimbolos );
				if ( i != -1 ) {
					geraMepa5( strRotulo, "NADA" );
					geraMepa2("ENRT", nvLexico, i ); // ENRT nvLexico, numVarsLocais_ao_Rotulo
				}
			}
			else {
				sprintf(mensagem, "Erro: rotulo \"%s\" inexistente ou fora de sincronia.", strAux);
				yyerror( mensagem );
				exit(0);
			}
		   }
		   else {
			sprintf(mensagem, "Erro: rotulo: \"%s\" nao declarado.", strAux);
			yyerror( mensagem );
			exit(1);
		   }
		  }
	comando_sem_rotulo
	| comando_sem_rotulo
;

comando_sem_rotulo:
	atribuicao 
	| chamada_procedimento
	| desvio
	| comando_composto
	| comando_condicional 
	| comando_repeticao
	| comando_read_write
;

comando_read_write:
	tREAD 
		{ isLeit = 1; }
	tABRE_PARENTESE lista_ids tFECHA_PARENTESE
		{ isLeit = 0;}
	| tWRITE 
		{ isImpr = 1; }
	  tABRE_PARENTESE lista_expressoes 
	  	{ if ( isChamProced ) {
 		  	geraMepa("IMPR");
 		  }
 		} 
	  tFECHA_PARENTESE
		{ isImpr = 0; }
;

atribuicao:
	variavel 
		{ // busca o tipo do elemento e o empilha para a analise semantica
		  i = busca_elemento( $1, p_tabSimbolos );
		  if ( i != -1 ) {
			  empilha_str( p_tabSimbolos->elem[i].tipo, p_strings );
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		}
	tATRIBUI expressao
		{ i = busca_elemento( $1, p_tabSimbolos );
		  if ( i != -1 ) {
		      if ( p_tabSimbolos->elem[i].categ == categ_pf ) {
		      	  if ( p_tabSimbolos->elem[i].u_categ.pf.passagem_parametros == por_refer ) {
		      		  geraMepa4("ARMI", p_tabSimbolos->elem[i].ender_lexico );
				  }
	   		      else if ( p_tabSimbolos->elem[i].u_categ.pf.passagem_parametros == por_valor ) { 
				      geraMepa4("ARMZ", p_tabSimbolos->elem[i].ender_lexico );
				  }
			  }
			  else { 
			  	  geraMepa4("ARMZ", p_tabSimbolos->elem[i].ender_lexico ); 
			  }

			  // analise semantica
			  i = compara_dois_ultimos_elementos( p_strings );
			  if ( !i ) {
			 	  yyerror( "Erro: Tipos sao diferentes" );
			 	  exit(1);
			  }
			  desempilha_str( strTipo, p_strings ); // esvazia a pilha para novas comparacoes
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		}
;

variavel: 
	tIDENTIFICADOR
;

lista_expressoes:
		{ if ( isChamProced ) {
 		  	  k++;
 		  }
 		}
 	expressao
 		{ if ( isImpr && !isChamProced ) {
 		  	geraMepa("IMPR");
 		  }
 		}
 	expressao_zrepet
;

expressao_zrepet:
	expressao_zrepet tVIRGULA 
		{ if ( isChamProced ) {
 		  	  k++;
 		  }
 		} 	
	expressao
		{ if ( isImpr && !isChamProced ) {
 		  	geraMepa("IMPR");
 		  }
 		}
	| vazio
;

/*
expressao_booleana:
	expressao
;
*/
		  
expressao: 
	expressao tIGUAL termo 
		{ geraMepa("CMIG"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}
	| expressao tDIFERENTE termo 
		{ geraMepa("CMDG"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}		
	| expressao tMENOR termo 
		{ geraMepa("CMME"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}
	| expressao tMAIOR termo 
		{ geraMepa("CMMA"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}
	| expressao tMENOR_OU_IGUAL  termo 
		{ geraMepa("CMEG"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}
	| expressao tMAIOR_OU_IGUAL  termo 
		{ geraMepa("CMAG"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}		
	| expressao tMAIS termo 
		{ geraMepa("SOMA"); 
		  // analise semantica
		  if ( isCondicional == 1 ) {
		  	yyerror( "Erro: Tipos sao diferentes" );
			exit(1);
		  }
		  else {
		  	i = compara_dois_ultimos_elementos( p_strings );
		  	if ( !i ) {
		  	 	yyerror( "Erro: Tipos sao diferentes" );
		  	  	exit(1);
		  	}
		  }
		 }
	| expressao tMENOS termo 
		{ geraMepa("SUBT"); 
		  // analise semantica
		  if ( isCondicional == 1 ) {
		  	yyerror( "Erro: Tipos sao diferentes" );
			exit(1);
		  }
		  else {
		  	i = compara_dois_ultimos_elementos( p_strings );
		  	if ( !i ) {
		  	 	yyerror( "Erro: Tipos sao diferentes" );
		  	  	exit(1);
		  	}
		  }
		 }
	| expressao tOR termo 
		{ geraMepa("DISJ"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}
	| termo
;

termo: 
	termo tMULTIPLICA fator
		{ geraMepa("MULT"); 
		  // analise semantica
		  if ( isCondicional == 1 ) {
		  	yyerror( "Erro: Tipos sao diferentes" );
			exit(1);
		  }
		  else {
		  	i = compara_dois_ultimos_elementos( p_strings );
		  	if ( !i ) {
		  	 	yyerror( "Erro: Tipos sao diferentes" );
		  	  	exit(1);
		  	}
		  }
		 }
	| termo tDIV fator 
		{ geraMepa("DIVI"); 
		  // analise semantica
		  if ( isCondicional == 1 ) {
		  	yyerror( "Erro: Tipos sao diferentes" );
			exit(1);
		  }
		  else {
		  	i = compara_dois_ultimos_elementos( p_strings );
		  	if ( !i ) {
		  	 	yyerror( "Erro: Tipos sao diferentes" );
		  	  	exit(1);
		  	}
		  }
		 }
	| termo tAND fator 
		{ geraMepa("CONJ"); 
		  // analise semantica
		  i = compara_relacao( p_strings );
		  if ( !i ) {
		  	  yyerror( "Erro: Tipos sao diferentes" );
		  	  exit(1);
		  }
		}
	| fator
;

fator:
	tABRE_PARENTESE expressao tFECHA_PARENTESE
	| tMENOS fator 
		{ geraMepa("INVR"); }
	| chamada_funcao
	| variavel
		{ i = busca_elemento( $1, p_tabSimbolos );
   		  if ( i != -1 ) {
		      if ( p_tabSimbolos->elem[i].categ == categ_pf ) {
		      	  if ( p_tabSimbolos->elem[i].u_categ.pf.passagem_parametros == por_refer ) {
		      	  	  if ( isChamProced && ( k >= 0 ) && ( pos >= 0 ) && ( k == pos ) )  {
		      	  	      // o PF e' por referencia e o argumento do proced. e' por referencia
				      	  geraMepa4( "CRVL", p_tabSimbolos->elem[i].ender_lexico );
		      	  	  }
		      	  	  else { 
						  // o PF e' por referencia e o argumento do proced. e' por valor
		      	  	      geraMepa4( "CRVI", p_tabSimbolos->elem[i].ender_lexico );
					  }
		      	  }
		      	  else if ( p_tabSimbolos->elem[i].u_categ.pf.passagem_parametros == por_valor ) {
				      if ( isChamProced && ( k >= 0 ) && ( pos >= 0 ) && ( k == pos ) )  {
					  	  // o PF e' por valor e o argumento do proced. e' por referencia
		      	  	  	  geraMepa4( "CREN", p_tabSimbolos->elem[i].ender_lexico );
		      	  	  }
		      	  	  else {
						  // o PF e' por valor e o argumento do proced. e' por valor
						  geraMepa4("CRVL", p_tabSimbolos->elem[i].ender_lexico );
					  }
		      	  }
		      }
		      else if ( p_tabSimbolos->elem[i].categ == categ_vs ) {
		       	  if ( isChamProced && ( k >= 0 ) && ( pos >= 0 ) && ( k == pos ) ) {
		          	  geraMepa4("CREN", p_tabSimbolos->elem[i].ender_lexico );
				  }
		          else
		          	  geraMepa4("CRVL", p_tabSimbolos->elem[i].ender_lexico );
		      }
		      else {  // procedimentos e function
		      	  geraMepa4("CRVL", p_tabSimbolos->elem[i].ender_lexico );
		      }
		      empilha_str( p_tabSimbolos->elem[i].tipo, p_strings ); // empilha o tipo para analise semantica
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }	
		}
	| tNUMERO
		{ geraMepa1("CRCT", $1); 
		  empilha_str( "integer", p_strings ); // empilha o tipo para analise semantica
		}
	| tNOT fator
		{ geraMepa("NEGA"); }
	| booleano
;

booleano:
	tTRUE
		{ geraMepa1("CRCT", 1);
		  empilha_str( "boolean", p_strings );
		}
	| tFALSE
		{ geraMepa1("CRCT", 0);
		  empilha_str( "boolean", p_strings );
		}
;

/*comando_repeticao:
	tWHILE expressao_booleana tDO comando_sem_rotulo
	| tREPEAT comando_sem_rotulo tUNTIL expressao_booleana
*/

comando_repeticao:
	tWHILE 
		{ isBlocoInterno = 1;
		  rotulo2 = proximo_int(); empilha_int( rotulo2, p_rotulos );
		  sprintf( strRotulo, "R%02d", rotulo2 );
		  geraMepa5 ( strRotulo, "NADA" ); 
		  isCondicional = 1;} 
	expressao 
		{ rotulo2 = proximo_int(); empilha_int( rotulo2, p_rotulos );
		  sprintf( strRotulo, "R%02d", rotulo2 );
		  geraMepa4( "DSVF", strRotulo );
		  isCondicional = 0;
		  }
	tDO comando_sem_rotulo
		{ rotulo2 = retorna_topo_pilha( p_rotulos );
		  sprintf( strRotulo, "R%02d", (rotulo2)-1 );
		  geraMepa4( "DSVS", strRotulo );
		  
		  sprintf( strRotulo, "R%02d", rotulo2 );
		  geraMepa5 (strRotulo, "NADA");
		  isBlocoInterno = 0;
		  }
	
	| tREPEAT comando_sem_rotulo tUNTIL expressao
;

parte_opcional:
	parte_decl_rotulos parte_decl_vars parte_decl_sub_rotinas
	| parte_decl_rotulos parte_decl_sub_rotinas  
	| parte_decl_vars parte_decl_sub_rotinas	
	| parte_decl_sub_rotinas
;

parte_decl_sub_rotinas:
	parte_decl_sub_rotinas proced_ou_funcao
	| vazio { 
	   	  rotulo1 = proximo_int(); empilha_int( rotulo1, p_rotulos ); 
	   	  sprintf( strRotulo, "R%02d", rotulo1 );
	   	  geraMepa4( "DSVS", strRotulo );
		 }
;

proced_ou_funcao:
	decl_procedimento tPONTO_E_VIRGULA | decl_funcao tPONTO_E_VIRGULA 
;

decl_procedimento:
	tPROCEDURE tIDENTIFICADOR 
		{ nvLexico++; endVar = -4; numPF = 0; isBlocoInterno = 0;

		  sprintf( elemTS.ident, " %d ", $2 );
		  i = busca_elemento_nivel( elemTS.ident, nvLexico, p_tabSimbolos );
 		  if ( i == -1 ) {  // nao achou outro simbolo igual, entao insere na TS		
		  	
			/* cria um rotulo para o procedimento*/
		  	rotulo1 = proximo_int(); empilha_int( rotulo1, p_rotulos ); 
		  	sprintf( strRotulo, "R%02d", rotulo1 );
		  	geraMepa5( strRotulo, "NADA" );  
		  	geraMepa1( "ENPR", nvLexico );
		  
		  	/* cria elemento do procedimento na tab. de simbolos */
		  	strcpy( elemTS.ident, $2 );
		  	elemTS.categ = categ_proc; 
		  	elemTS.nivel = nvLexico; 
		  	strcpy( elemTS.ender_lexico, "\0" );
		  	strcpy( elemTS.tipo, "proc" );
		  	strcpy( elemTS.u_categ.proc.rot_interno, strRotulo );
		  	strcpy( strRotuloPaidoLabel, strRotulo );
		  	empilha( elemTS, p_tabSimbolos );
		  	desempilha_int( p_rotulos );
		  }
		  else {
			sprintf( mensagem, "Erro: a declaracao do procedimento \"%s\" duplicada para o mesmo nivel lexico", elemTS.ident );
			yyerror( mensagem );
			exit(1);
		  }

		}	
	parametros_formais tPONTO_E_VIRGULA 
		{ endVar = -1; 
		  /* busca o procedimento para setar o seu nivel lexico e o seu numero de params. formais */
		  i = busca_elemento( $2, p_tabSimbolos );
		  if ( i != -1 ) {
		  	  p_tabSimbolos->elem[i].u_categ.proc.numPF = numPF;
		  	  p_tabSimbolos->elem[i].u_categ.proc.parametros = vetAux;
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" declarado.", $2 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		 }
	bloco
		{ // Verifica se ha vars locais, pois proced pode nao ter vars declar localmente
		  i = busca_elemento( $2, p_tabSimbolos );
		  numVar = busca_numero_varslocal_ao_rotulo( p_tabSimbolos->elem[i].u_categ.proc.rot_interno , p_tabSimbolos );
		  if ( numVar != 0 ) {
			numVar = desempilha_int( p_vars );
			geraMepa1( "DMEM", numVar );
		  }

		  /* busca o nivel lexico do procedimento e o numero de params. formais */
		  if ( i != -1 ) {
		  	  numPF = p_tabSimbolos->elem[i].u_categ.proc.numPF;
		  	  geraMepa2( "RTPR", p_tabSimbolos->elem[i].nivel, numPF );
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $2 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		 desempilha_elementos_coletor( numVar + numPF, nvLexico, p_tabSimbolos );
		 nvLexico--;
		 }
;
		 //tFUNCTION tIDENTIFICADOR parametros_formais tDOIS_PONTOS tipo tPONTO_E_VIRGULA
decl_funcao:
	tFUNCTION tIDENTIFICADOR 
		{ nvLexico++; endVar = -4; numPF = 0; isBlocoInterno = 0; isDeclFunc = 1;
		
		  sprintf( elemTS.ident, " %d ", $2 );
		  i = busca_elemento_nivel( elemTS.ident, nvLexico, p_tabSimbolos );
 		  if ( i == -1 ) {  // nao achou outro simbolo igual, entao insere na TS

		  	/* cria um rotulo para o procedimento*/
		  	rotulo1 = proximo_int(); empilha_int( rotulo1, p_rotulos ); 
		  	sprintf( strRotulo, "R%02d", rotulo1 );
		  	geraMepa5( strRotulo, "NADA" );  
		  	geraMepa1( "ENPR", nvLexico );
		  
		  	/* cria elemento da funcao na tab. de simbolos */
		  	strcpy( elemTS.ident, $2 );
 		  	elemTS.categ = categ_func; 
		  	elemTS.nivel = nvLexico; 
		  	strcpy( elemTS.tipo, "integer" );
		  	strcpy( elemTS.u_categ.func.rot_interno, strRotulo );
		  	strcpy( strRotuloPaidoLabel, strRotulo );
		  	empilha( elemTS, p_tabSimbolos );
		  	desempilha_int( p_rotulos );
		  }
		  else {
			sprintf( mensagem, "Erro: a declaracao da funcao \"%s\" duplicada para o mesmo nivel lexico", elemTS.ident );
			yyerror( mensagem );
			exit(1);
		  }
		}	
	parametros_formais 
		{
		  /* busca a funcao para setar o seu ender lexico e o seu numero de params. formais */
		  i = busca_elemento( $2, p_tabSimbolos );
		  if ( i != -1 ) {
		  	p_tabSimbolos->elem[i].u_categ.func.numPF = numPF;
		  	p_tabSimbolos->elem[i].u_categ.func.parametros = vetAux1;
			sprintf( p_tabSimbolos->elem[i].ender_lexico, "%d, %d", nvLexico, endVar );
		  }
		  else {  
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado", $2 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		  endVar = -1;
		}
	tDOIS_PONTOS tipo tPONTO_E_VIRGULA

	bloco
		{ // Verifica se ha vars locais, pois funct pode nao ter vars declar localmente
		  i = busca_elemento( $2, p_tabSimbolos );
		  numVar = busca_numero_varslocal_ao_rotulo( p_tabSimbolos->elem[i].u_categ.func.rot_interno , p_tabSimbolos );
		  if ( numVar != 0 ) {
			numVar = desempilha_int( p_vars );
			geraMepa1( "DMEM", numVar );
		  }
		  
		  /* busca o nivel lexico da funcao e o numero de params. formais */
		  if ( i != -1 ) {
			  numPF = p_tabSimbolos->elem[i].u_categ.func.numPF;
		  	  geraMepa2( "RTPR", p_tabSimbolos->elem[i].nivel, numPF );
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao declarado.", $2 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		  desempilha_elementos_coletor( numVar + numPF, nvLexico, p_tabSimbolos );
		  nvLexico--;
		  isDeclFunc = 0;
		}
;

parametros_formais:
		{ isPF = 1; j = -1; }
	tABRE_PARENTESE secao_params_formais sec_params_formais_zrepet tFECHA_PARENTESE
		{ /* copia os elementos da tabela auxiliar para a tabela de simbolos e
		     ajusta o endereco lexico dos params. formais;
		     */
	  	  for ( i = p_tabSimbolos_aux->topo; i >= 0; i-- ) {
			   elemTS_aux = desempilha( p_tabSimbolos_aux );
			   sprintf( elemTS_aux.ender_lexico, "%d, %d", nvLexico, endVar );
			   empilha( elemTS_aux, p_tabSimbolos );
			   endVar--;
		  }		
		  isPF = 0; }
	| vazio
;

sec_params_formais_zrepet:
	sec_params_formais_zrepet tPONTO_E_VIRGULA secao_params_formais
	| vazio
;

secao_params_formais:
	tVAR 
		{ isPorRefer = 1; }
	lista_ids 
		{ numPF = numPF + contIds; }
	tDOIS_PONTOS tIDENTIFICADOR
		{ /* insere o tipo do identificador dos elementos na TS  */  	  
	  	  for ( i = p_tabSimbolos_aux->topo; i >= ( ( p_tabSimbolos_aux->topo + 1 ) - numPF ); i-- ) {
			   strcpy( p_tabSimbolos_aux->elem[i].tipo, $6 );
		  }
	  	  isPorRefer = 0;
	  	}
	| 	  { isPorValor = 1; }
	  lista_ids 
	  		{ numPF = numPF + contIds; }
	  tDOIS_PONTOS tIDENTIFICADOR
	  	  { /* insere o tipo do identificador dos elementos na TS  */  	  
	  	    for ( i = p_tabSimbolos_aux->topo; i >= ( ( p_tabSimbolos_aux->topo + 1 ) - numPF ); i-- ) {
			     strcpy( p_tabSimbolos_aux->elem[i].tipo, $5 ); 
			}
			isPorValor = 0;
	  	  }
;

chamada_procedimento:
	tIDENTIFICADOR 

		{ isChamProced = 1; pos = -1;
		  i = busca_elemento( $1, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_proc ) ) {
			  /* busca posicao do elemento com passagem parametros por referencia */
			  pAux = p_tabSimbolos->elem[i].u_categ.proc.parametros;
			  for ( k = 0; k < p_tabSimbolos->elem[i].u_categ.proc.numPF; k++ ) {
			  	   if ( *( pAux + k ) == por_refer ) {
			  	       pos = k;
			  	       break;
			  	   }
			  	   else {
			  	       continue;
			  	   }
			  }
			  k = -1;
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao e' um procedimento.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }		  
		}	
	tABRE_PARENTESE lista_expressoes tFECHA_PARENTESE
		{ i = busca_elemento( $1, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_proc ) ) {
			if ( p_tabSimbolos->elem[i].u_categ.proc.numPF == k+1 ) { // semantica
				sprintf( strAux, "%s, %d", p_tabSimbolos->elem[i].u_categ.proc.rot_interno, nvLexico );
				geraMepa4("CHPR", strAux );
			}
			else {
				sprintf (mensagem, "Erro: O numero invalido de argumentos do procedimento \"%s\".", $1);
				yyerror( mensagem );
				exit(1);
			}
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao e' um procedimento.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		  isChamProced = 0;
		}
	| tIDENTIFICADOR
		{ i = busca_elemento( $1, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_proc ) ) {
			sprintf( strAux, "%s, %d", p_tabSimbolos->elem[i].u_categ.proc.rot_interno, nvLexico );
			geraMepa4("CHPR", strAux );
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao e' um procedimento.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  	}
		  }	
;

chamada_funcao:
	tIDENTIFICADOR tABRE_PARENTESE 

		{ isChamProced = 1; pos = -1;
		  geraMepa1( "AMEM", 1 );
		  i = busca_elemento( $1, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_func ) ) {
			  /* busca posicao do elemento com passagem parametros por referencia */
			  pAux = p_tabSimbolos->elem[i].u_categ.func.parametros;
			  for ( k = 0; k < p_tabSimbolos->elem[i].u_categ.func.numPF; k++ ) {
			  	   if ( *( pAux + k ) == por_refer ) {
			  	       pos = k;
			  	       break;
			  	   }
			  	   else {
			  	       continue;
			  	   }
			  }
			  k = -1;
		  }
		  else { 
			  sprintf( mensagem, "Erro: Elemento \"%s\" nao e' uma funcao.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }		  
		}
	lista_expressoes tFECHA_PARENTESE
		{ i = busca_elemento( $1, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_func ) ) {
			if ( p_tabSimbolos->elem[i].u_categ.func.numPF == k+1 ) { // semantica
				sprintf( strAux, "%s, %d", p_tabSimbolos->elem[i].u_categ.func.rot_interno, nvLexico );
		     		geraMepa4("CHPR", strAux );
			}
			else {
				sprintf (mensagem, "Erro: O numero invalido de argumentos da funcao \"%s\".", $1);
				yyerror( mensagem );
				exit(1);
			}
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Elemento \"%s\" nao e' uma funcao!", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		  isChamProced = 0;
		}
	| tIDENTIFICADOR tABRE_PARENTESE tFECHA_PARENTESE
		{ i = busca_elemento( $1, p_tabSimbolos );
		  if ( ( i != -1 ) && ( p_tabSimbolos->elem[i].categ == categ_func ) ) {
			if ( p_tabSimbolos->elem[i].u_categ.func.numPF == k+1 ) { // semantica
				sprintf( strAux, "%s, %d", p_tabSimbolos->elem[i].u_categ.func.rot_interno, nvLexico );
				geraMepa4("CHPR", strAux );
			}
			else {
				sprintf (mensagem, "Erro: a funcao \"%s\" recebe argumentos porem foi declada sem parametros.", $1);
				yyerror( mensagem );
				exit(1);
			}
		 }
		 else {
		  	  sprintf( mensagem, "Erro: Elemento %s nao e' uma funcao.", $1 );
			  yyerror( mensagem );
		  	  exit(1);
		  }
		}
;

parte_decl_rotulos:
	tLABEL tNUMERO
		{ // cria um rotulo para o label
		  rotulo1 = proximo_int(); empilha_int( rotulo1, p_rotulos );
		  sprintf( strRotulo, "R%02d", rotulo1 );

		  sprintf( elemTS.ident, " %d ", $2 );
		  i = busca_elemento_nivel( elemTS.ident, nvLexico, p_tabSimbolos );
 		  if ( i == -1 ) {  // nao achou outro simbolo igual, entao insere na TS

		  	// cria elemento label na tab. de simbolos
		  	sprintf( elemTS.ident, " %d ", $2 );
		  	elemTS.categ = categ_label; 
		  	elemTS.nivel = nvLexico;
		  	strcpy( elemTS.ender_lexico, "\0" );
		  	strcpy( elemTS.tipo, "label" );
		  	strcpy( elemTS.u_categ.lab.rot_interno, strRotulo );
		  	if (nvLexico == 0 ) {
				strcpy( elemTS.u_categ.lab.rot_pai, "R00" );
			  }
			  else { 
				  strcpy( elemTS.u_categ.lab.rot_pai, strRotuloPaidoLabel );
				}
		  	empilha( elemTS, p_tabSimbolos );
		  	desempilha_int( p_rotulos );
		  }
		  else {
			sprintf( mensagem, "Erro: Declaracao do Label \"%s\" duplicada", elemTS.ident );
			yyerror( mensagem );
			exit(1);
		  }
		}
	numero_zrepet tPONTO_E_VIRGULA
;

numero_zrepet:
	numero_zrepet tVIRGULA tNUMERO
		{ // cria um rotulo para o label
		  rotulo1 = proximo_int(); empilha_int( rotulo1, p_rotulos );
		  sprintf( strRotulo, "R%02d", rotulo1 );

		  sprintf( elemTS.ident, " %d ", $3 );
		  i = busca_elemento_nivel( elemTS.ident, nvLexico, p_tabSimbolos );
 		  if ( i == -1 ) {  // nao achou outro simbolo igual, entao insere na TS

		  	// cria elemento label na tab. de simbolos
		  	sprintf( elemTS.ident, " %d ", $3 );
		  	elemTS.categ = categ_label; 
		  	elemTS.nivel = nvLexico;
		  	strcpy( elemTS.ender_lexico, "\0" );
		  	strcpy( elemTS.tipo, "label" );
		  	strcpy( elemTS.u_categ.lab.rot_interno, strRotulo );
			if (nvLexico == 0 ) {
				strcpy( elemTS.u_categ.lab.rot_pai, "R00" );
			  }
			  else { 
				strcpy( elemTS.u_categ.lab.rot_pai, strRotuloPaidoLabel );
				}
		  	empilha( elemTS, p_tabSimbolos );
		  	desempilha_int( p_rotulos ); 
		  }
		  else {
			sprintf( mensagem, "Erro: Declaracao do Label \"%s\" duplicada", elemTS.ident );
			yyerror( mensagem );
			exit(1);
		  }
		}
	| vazio
;

desvio:
	tGOTO tNUMERO
		{ 
		  sprintf( strAux, " %d ", $2 );
		  i = busca_elemento( strAux, p_tabSimbolos );
		  if ( i != -1 ) {
			if ( p_tabSimbolos->elem[i].categ == categ_label ) {
				if ( nvLexico >= p_tabSimbolos->elem[i].nivel ) { // GOTO nunca sobe de nivel
					// DSVR ROT, nLexDestino, nLexChamador
					geraMepa6("DSVR", p_tabSimbolos->elem[i].u_categ.lab.rot_interno, p_tabSimbolos->elem[i].nivel, nvLexico );
				}
				else {
				sprintf( mensagem, "Erro: Label \"%s\" nao esta' declarado abaixo do Nivel Lexico do Chamador.", strAux);
				yyerror( mensagem );
		  	  	exit(1);
				}
		  	}
		  }
		  else {
		  	  sprintf( mensagem, "Erro: Label \"%s\" nao declarado.", strAux );
			  yyerror( mensagem );
		  	  exit(1);
		     }
		  }
;

//comando_condicional: tIF expressa_booleana tTHEN comando_sem_rotulo cond_else

comando_condicional: 
	tIF { isCondicional = 1; } expressao 
		{ 
		  isBlocoInterno = 1;
		  rotulo1 = proximo_int(); empilha_int( rotulo1, p_rotulos );
		  rotulo2 = proximo_int(); 
		  sprintf( strRotulo, "R%02d", rotulo2 );
		  geraMepa4( "DSVF", strRotulo ); // para o else (se existir)
		  isCondicional = 0;
		}
	tTHEN comando_sem_rotulo 
			{ 
			  sprintf( strRotulo, "R%02d", rotulo1 ); // p/o final do if
		  	  geraMepa4( "DSVS", strRotulo );
			  sprintf( strRotulo, "R%02d", rotulo2 );
		  	  geraMepa5 (strRotulo, "NADA");
			}
	cond_else
		{ 
		  rotulo1 = desempilha_int( p_rotulos );
		  sprintf( strRotulo, "R%02d", rotulo1 );
		  geraMepa5 (strRotulo, "NADA");
		  isBlocoInterno = 0;
		}
;
cond_else:
	tELSE 
		{ isBlocoInterno = 1; }
	comando_sem_rotulo
	| %prec LOWER_THAN_ELSE
;

%%
#include "lex.yy.c"


int main( int argc, char** argv) {

	int res;
	
	++argv, --argc;

	printf( "Compilador para Pascal Simplificado\n" );
	printf( "Copyleft (c) 2008 by Andre, Elisangela, Roberto\n" );
	printf( "Target OS: Linux for i386\n" );

	if ( argc > 0 ) {
		strcpy( arquivo_para_compilar, argv[0] );
       	if ( ( yyin = fopen( arquivo_para_compilar, "r" )) == NULL ) {
			fprintf( stderr, "Erro: Nao consigo abrir o arquivo \"%s\"\n", arquivo_para_compilar );
			fprintf( stderr, "Erro: Compilacao abortada\n" );
    	    exit(1);
		}
    }
    else
        yyin = stdin;

	// abre o arquivo que contera' o codigo mepa
	if ( ( fp = fopen( ARQUIVO, "w" ) ) == NULL ) {
		fprintf( stderr, "Erro: Nao consigo abrir o arquivo \"%s\"\n", ARQUIVO );
		fprintf( stderr, "Erro: Compilacao abortada\n" );
    	exit(1);
	}
	
	// ajusta ponteiros e inicializa as tabelas de simbolos
	p_tabSimbolos = &tabSimbolos;
	p_tabSimbolos_aux = &tabSimbolos_aux;
	fazPilhaVazia( p_tabSimbolos );
	fazPilhaVazia( p_tabSimbolos_aux );
	
	// ajusta ponteiros e inicializa as pilhas
	p_rotulos = &pilha_rotulos;
	p_vars = &pilha_vars;
	p_strings = &pilha_strings;
	fazPilhaVazia_int( p_rotulos );
	fazPilhaVazia_int( p_vars );
	fazPilhaVazia_str( p_strings );
	
	printf( "Compilando %s\n", arquivo_para_compilar );

	res = yyparse();
	
	fclose( yyin );
	fclose( fp );
	
	if ( !res )
		printf( "*** Arquivo \"%s\" gerado com sucesso. ***\n", ARQUIVO );
	else {
		#ifdef DEPURA
			imprime_pilha_ints( p_rotulos, "Rotulos - Final" );
		
			imprime_tabela_de_simbolos( p_tabSimbolos );
		
			imprime_pilha_strs( p_strings, "Tipos das Expressoes" );
		#endif
		exit(1);
	}

	#ifdef DEPURA
		imprime_pilha_ints( p_rotulos, "Rotulos - Final" );
	
		imprime_tabela_de_simbolos( p_tabSimbolos );
	
		imprime_pilha_strs( p_strings, "Tipos das Expressoes" );
	#endif
	return res;
}


int yyerror( char* errmsg ) {
	fprintf( stderr, "%s (linha: %d) %s\n", arquivo_para_compilar, yylineno, errmsg );
	fprintf( stderr, "Erro: Compilacao abortada\n" );
	return 0;
}
